/* 
 * Copyright 2005-2014 Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include <ChannelControl.h>
#include <PropertyInfo.h>

#include <map>
#include <string>

struct limit_label {
	std::string min_label;
	std::string max_label;
};

typedef std::map<int32, limit_label> label_map;

static property_info
sPropertyInfo[] = {
	{ "ChannelCount",
		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
		{ B_DIRECT_SPECIFIER, 0 }, NULL, 0, { B_INT32_TYPE }
	},

	{ "CurrentChannel",
		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
		{ B_DIRECT_SPECIFIER, 0 }, NULL, 0, { B_INT32_TYPE }
	},

	{ "MaxLimitLabel", 
		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
		{ B_DIRECT_SPECIFIER, 0 }, NULL, 0, { B_STRING_TYPE }
	},

	{ "MinLimitLabel",
		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
		{ B_DIRECT_SPECIFIER, 0 }, NULL, 0, { B_STRING_TYPE }
	},

	{ 0 }
};


BChannelControl::BChannelControl(BRect frame, const char* name,
	const char* label, BMessage* model, int32 channel_count,
	uint32 resizingMode, uint32 flags)
	:
	BControl(frame, name, label, model, resizingMode, flags),
	fChannelCount(channel_count),
	fCurrentChannel(0),
	fChannelMin(NULL),
	fChannelMax(NULL),
	fChannelValues(NULL),
	fMultiLabels(NULL),
	fModificationMsg(NULL)
{
	fChannelMin = new int32[channel_count];
	memset(fChannelMin, 0, sizeof(int32) * channel_count);

	fChannelMax = new int32[channel_count];
	for (int32 i = 0; i < channel_count; i++)
		fChannelMax[i] = 100;

	fChannelValues = new int32[channel_count];
	memset(fChannelValues, 0, sizeof(int32) * channel_count);

	fMultiLabels = (void*)new label_map;
}


BChannelControl::BChannelControl(const char* name, const char* label,
	BMessage* model, int32 channelCount, uint32 flags)
	:
	BControl(name, label, model, flags),
	fChannelCount(channelCount),
	fCurrentChannel(0),
	fChannelMin(NULL),
	fChannelMax(NULL),
	fChannelValues(NULL),
	fMultiLabels(NULL),
	fModificationMsg(NULL)
{
	fChannelMin = new int32[channelCount];
	memset(fChannelMin, 0, sizeof(int32) * channelCount);
	
	fChannelMax = new int32[channelCount];
	for (int32 i = 0; i < channelCount; i++)
		fChannelMax[i] = 100;

	fChannelValues = new int32[channelCount];
	memset(fChannelValues, 0, sizeof(int32) * channelCount);

	fMultiLabels = (void*)new label_map;
}


BChannelControl::BChannelControl(BMessage* archive)
	:
	BControl(archive),
	fChannelCount(0),
	fCurrentChannel(0),
	fChannelMin(NULL),
	fChannelMax(NULL),
	fChannelValues(NULL),
	fMultiLabels(NULL),
	fModificationMsg(NULL)
{
	archive->FindInt32("be:_m_channel_count", &fChannelCount);
	archive->FindInt32("be:_m_value_channel", &fCurrentChannel);

	if (fChannelCount > 0) {
		fChannelMin = new int32[fChannelCount];
		memset(fChannelMin, 0, sizeof(int32) * fChannelCount);

		fChannelMax = new int32[fChannelCount];
		for (int32 i = 0; i < fChannelCount; i++)
			fChannelMax[i] = 100;

		fChannelValues = new int32[fChannelCount];
		memset(fChannelValues, 0, sizeof(int32) * fChannelCount);

		for (int32 c = 0; c < fChannelCount; c++) {
			archive->FindInt32("be:_m_channel_min", c, &fChannelMin[c]);
			archive->FindInt32("be:_m_channel_max", c, &fChannelMax[c]);
			archive->FindInt32("be:_m_channel_val", c, &fChannelValues[c]);
		}
	}

	const char* label = NULL;
	if (archive->FindString("be:_m_min_label", &label) == B_OK)
		fMinLabel = label;

	if (archive->FindString("be:_m_max_label", &label) == B_OK)
		fMaxLabel = label;

	BMessage* modificationMessage = new BMessage;
	if (archive->FindMessage("_mod_msg", modificationMessage) == B_OK)
		fModificationMsg = modificationMessage;
	else
		delete modificationMessage;

	fMultiLabels = (void*)new label_map;
}


BChannelControl::~BChannelControl()
{
	delete[] fChannelMin;
	delete[] fChannelMax;
	delete[] fChannelValues;
	delete fModificationMsg;
	delete reinterpret_cast<label_map*>(fMultiLabels);
}


status_t
BChannelControl::Archive(BMessage* data, bool deep) const
{
	status_t status = BControl::Archive(data, deep);
	if (status == B_OK)
		status = data->AddInt32("be:_m_channel_count", fChannelCount);

	if (status == B_OK)
		status = data->AddInt32("be:_m_value_channel", fCurrentChannel);

	if (status == B_OK)
		status = data->AddString("be:_m_min_label", fMinLabel.String());

	if (status == B_OK)
		status = data->AddString("be:_m_max_label", fMaxLabel.String());

	if (status == B_OK && fChannelValues != NULL
		&& fChannelMax != NULL && fChannelMin != NULL) {
		for (int32 i = 0; i < fChannelCount; i++) {
			status = data->AddInt32("be:_m_channel_min", fChannelMin[i]);
			if (status < B_OK)
				break;

			status = data->AddInt32("be:_m_channel_max", fChannelMax[i]);
			if (status < B_OK)
				break;

			status = data->AddInt32("be:_m_channel_val", fChannelValues[i]);
			if (status < B_OK)
				break;
		}
	}

	return status;
}


void
BChannelControl::FrameResized(float newWidth, float newHeight)
{
	BView::FrameResized(newWidth, newHeight);
}


void
BChannelControl::SetFont(const BFont* font, uint32 mask)
{
	BView::SetFont(font, mask);
}


void
BChannelControl::AttachedToWindow()
{
	BControl::AttachedToWindow();
}


void
BChannelControl::DetachedFromWindow()
{
	BControl::DetachedFromWindow();
}


void
BChannelControl::ResizeToPreferred()
{
	BControl::ResizeToPreferred();
}


void
BChannelControl::MessageReceived(BMessage* message)
{
	BControl::MessageReceived(message);
}


BHandler*
BChannelControl::ResolveSpecifier(BMessage* message, int32 index,
	BMessage* specifier, int32 what, const char* property)
{
	BHandler* target = this;
	BPropertyInfo propertyInfo(sPropertyInfo);
	if (propertyInfo.FindMatch(message, index, specifier, what, property)
			< B_OK) {
		target = BControl::ResolveSpecifier(message, index, specifier,
			what, property);
	}

	return target;
}


status_t
BChannelControl::GetSupportedSuites(BMessage* data)
{
	if (data == NULL)
		return B_BAD_VALUE;

	status_t err = data->AddString("suites", "suite/vnd.Be-channel-control");

	BPropertyInfo propertyInfo(sPropertyInfo);
	if (err == B_OK) 
		err = data->AddFlat("messages", &propertyInfo);

	if (err == B_OK)
		return BControl::GetSupportedSuites(data);

	return err;
}


void
BChannelControl::SetModificationMessage(BMessage* message)
{
	delete fModificationMsg;
	fModificationMsg = message;
}


BMessage*
BChannelControl::ModificationMessage() const
{
	return fModificationMsg;
}


status_t
BChannelControl::Invoke(BMessage* message)
{
	bool notify = false;
	BMessage invokeMessage(InvokeKind(&notify));

	if (message != NULL)
		invokeMessage = *message;
	else if (Message() != NULL)
		invokeMessage = *Message();
	
	invokeMessage.AddInt32("be:current_channel", fCurrentChannel);

	return BControl::Invoke(&invokeMessage);
}


status_t
BChannelControl::InvokeChannel(BMessage* message, int32 fromChannel,
	int32 channelCount, const bool* _mask)
{
	bool notify = false;
	BMessage invokeMessage(InvokeKind(&notify));

	if (message != NULL)
		invokeMessage = *message;
	else if (Message() != NULL)
		invokeMessage = *Message();

	invokeMessage.AddInt32("be:current_channel", fCurrentChannel);
	if (channelCount < 0)
		channelCount = fChannelCount - fromChannel;

	for (int32 i = 0; i < channelCount; i++) {
		invokeMessage.AddInt32("be:channel_value",
			fChannelValues[fromChannel + i]);
		invokeMessage.AddBool("be:channel_changed", _mask ? _mask[i] : true);
	}

	return BControl::Invoke(&invokeMessage);
}


status_t
BChannelControl::InvokeNotifyChannel(BMessage* message, uint32 kind,
	int32 fromChannel, int32 channelCount, const bool* _mask)
{
	BeginInvokeNotify(kind);
	status_t status = InvokeChannel(message, fromChannel, channelCount, _mask);
	EndInvokeNotify();

	return status;
}


void
BChannelControl::SetValue(int32 value)
{
	// Get real
	if (value > fChannelMax[fCurrentChannel])
		value = fChannelMax[fCurrentChannel];

	if (value < fChannelMin[fCurrentChannel])
		value = fChannelMin[fCurrentChannel];

	if (value != fChannelValues[fCurrentChannel]) {
		StuffValues(fCurrentChannel, 1, &value);
		BControl::SetValue(value);
	}
}


status_t
BChannelControl::SetCurrentChannel(int32 channel)
{
	if (channel < 0 || channel >= fChannelCount)
		return B_BAD_INDEX;

	if (channel != fCurrentChannel) {
		fCurrentChannel = channel;
		BControl::SetValue(fChannelValues[fCurrentChannel]);
	}

	return B_OK;	
}


int32
BChannelControl::CurrentChannel() const
{
	return fCurrentChannel;
}


int32
BChannelControl::CountChannels() const
{
	return fChannelCount;
}


status_t
BChannelControl::SetChannelCount(int32 channel_count)
{
	if (channel_count < 0 || channel_count >= MaxChannelCount())
		return B_BAD_VALUE;

	// TODO: Currently we only grow the buffer. Test what BeOS does
	if (channel_count > fChannelCount) {
		int32* newMin = new int32[channel_count];
		int32* newMax = new int32[channel_count];
		int32* newVal = new int32[channel_count];

		memcpy(newMin, fChannelMin, fChannelCount);
		memcpy(newMax, fChannelMax, fChannelCount);
		memcpy(newVal, fChannelValues, fChannelCount);

		delete[] fChannelMin;
		delete[] fChannelMax;
		delete[] fChannelValues;

		fChannelMin = newMin;
		fChannelMax = newMax;
		fChannelValues = newVal;
	}

	fChannelCount = channel_count;

	return B_OK;
}


int32
BChannelControl::ValueFor(int32 channel) const
{
	int32 value = 0;
	if (GetValue(&value, channel, 1) <= 0)
		return -1;

	return value;
}


int32
BChannelControl::GetValue(int32* outValues, int32 fromChannel,
	int32 channelCount) const
{
	int32 i = 0;
	for (i = 0; i < channelCount; i++)
		outValues[i] = fChannelValues[fromChannel + i];

	return i;
}


status_t
BChannelControl::SetValueFor(int32 channel, int32 value)
{
	return SetValue(channel, 1, &value);
}


status_t
BChannelControl::SetValue(int32 fromChannel, int32 channelCount,
	const int32* values)
{
	return StuffValues(fromChannel, channelCount, values);
}


status_t
BChannelControl::SetAllValue(int32 values)
{
	int32* newValues = new int32[fChannelCount];
	for (int32 i = 0; i < fChannelCount; i++) {
		int32 limitedValue = max_c(values, MinLimitList()[i]);
		limitedValue = min_c(limitedValue, MaxLimitList()[i]);

		newValues[i] = limitedValue;
	}

	delete[] fChannelValues;
	fChannelValues = newValues;
	BControl::SetValue(fChannelValues[fCurrentChannel]);

	return B_OK;
}


status_t
BChannelControl::SetLimitsFor(int32 channel, int32 minimum, int32 maximum)
{
	return SetLimitsFor(channel, 1, &minimum, &maximum);
}


status_t
BChannelControl::GetLimitsFor(int32 channel, int32* minimum,
	int32* maximum) const
{
	return GetLimitsFor(channel, 1, minimum, maximum);
}


status_t
BChannelControl::SetLimitsFor(int32 fromChannel, int32 channelCount,
	const int32* minimum, const int32* maximum)
{
	if (fromChannel + channelCount > CountChannels())
		channelCount = CountChannels() - fromChannel;

	for (int i = 0; i < channelCount; i++) {
		if (minimum[i] > maximum[i])
			return B_BAD_VALUE;

		fChannelMin[fromChannel + i] = minimum[i];
		fChannelMax[fromChannel + i] = maximum[i];
		if (fChannelValues[fromChannel + i] < minimum[i])
			fChannelValues[fromChannel + i] = minimum[i];
		else if (fChannelValues[fromChannel + i] > maximum[i])
			fChannelValues[fromChannel + i] = maximum[i];
	}

	return B_OK;
}


status_t
BChannelControl::GetLimitsFor(int32 fromChannel, int32 channelCount,
	int32* minimum, int32* maximum) const
{
	if (minimum == NULL || maximum == NULL)
		return B_BAD_VALUE;

	if (fChannelMin == NULL || fChannelMax == NULL)
		return B_ERROR;
	if (fromChannel + channelCount > CountChannels())
		channelCount = CountChannels() - fromChannel;

	for (int i = 0; i < channelCount; i++) {
		minimum[i] = fChannelMin[fromChannel + i];
		maximum[i] = fChannelMax[fromChannel + i];
	}

	return B_OK;
}


status_t
BChannelControl::SetLimits(int32 minimum, int32 maximum)
{
	if (minimum > maximum)
		return B_BAD_VALUE;

	int32 numChannels = CountChannels();

	for (int32 c = 0; c < numChannels; c++) {
		fChannelMin[c] = minimum;
		fChannelMax[c] = maximum;
		if (fChannelValues[c] < minimum)
			fChannelValues[c] = minimum;
		else if (fChannelValues[c] > maximum)
			fChannelValues[c] = maximum;
	}

	return B_OK;
}


status_t
BChannelControl::GetLimits(int32* outMinimum, int32* outMaximum) const
{
	if (outMinimum == NULL || outMaximum == NULL)
		return B_BAD_VALUE;

	if (fChannelMin == NULL || fChannelMax == NULL)
		return B_ERROR;

	int32 numChannels = CountChannels();
	for (int32 c = 0; c < numChannels; c++) {
		outMinimum[c] = fChannelMin[c];
		outMaximum[c] = fChannelMax[c];
	}

	return B_OK;
}


status_t
BChannelControl::SetLimitLabels(const char* minLabel, const char* maxLabel)
{
	if (minLabel != fMinLabel)
		fMinLabel = minLabel;

	if (maxLabel != fMaxLabel)
		fMaxLabel = maxLabel;

	Invalidate();

	return B_OK;
}


const char*
BChannelControl::MinLimitLabel() const
{
	return fMinLabel.String();
}


const char*
BChannelControl::MaxLimitLabel() const
{
	return fMaxLabel.String();
}


status_t
BChannelControl::SetLimitLabelsFor(int32 channel, const char* minLabel,
	const char* maxLabel)
{
	(*(label_map*)fMultiLabels)[channel].max_label = maxLabel;
	(*(label_map*)fMultiLabels)[channel].min_label = minLabel;
	return B_OK;
}


status_t
BChannelControl::SetLimitLabelsFor(int32 fromChannel, int32 channelCount,
	const char* minLabel, const char* maxLabel)
{
	for (int32 i = fromChannel; i < fromChannel + channelCount; i++) {
		SetLimitLabelsFor(i, minLabel, maxLabel);
	}
	return B_OK;
}


const char*
BChannelControl::MinLimitLabelFor(int32 channel) const
{
	if (fMultiLabels != NULL) {
		label_map::const_iterator iter = ((label_map*)fMultiLabels)->find(channel);
		if (iter != ((label_map*)fMultiLabels)->end())
			return (*iter).second.min_label.c_str();
	}
	return NULL;
}


const char*
BChannelControl::MaxLimitLabelFor(int32 channel) const
{
	if (fMultiLabels != NULL) {
		label_map::const_iterator iter = ((label_map*)fMultiLabels)->find(channel);
		if (iter != ((label_map*)fMultiLabels)->end())
			return (*iter).second.max_label.c_str();
	}
	return NULL;
}


status_t
BChannelControl::StuffValues(int32 fromChannel, int32 channelCount,
	const int32* values)
{
	if (values == NULL)
		return B_BAD_VALUE;

	if (fromChannel < 0 || fromChannel > fChannelCount
		|| fromChannel + channelCount > fChannelCount) {
		return B_BAD_INDEX;
	}

	for (int32 i = 0; i < channelCount; i++) {
		if (values[i] <= fChannelMax[fromChannel + i]
			&& values[i] >= fChannelMin[fromChannel + i]) {
			fChannelValues[fromChannel + i] = values[i];
		}
	}

	// if the current channel was updated, update also the control value
	if (fCurrentChannel >= fromChannel
		&& fCurrentChannel <= fromChannel + channelCount) {
		BControl::SetValue(fChannelValues[fCurrentChannel]);
	}

	return B_OK;
}


void BChannelControl::_Reserverd_ChannelControl_0(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_1(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_2(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_3(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_4(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_5(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_6(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_7(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_8(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_9(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_10(void*, ...) {}
void BChannelControl::_Reserverd_ChannelControl_11(void*, ...) {}
